package gptma.bo.dao;

import gptma.bo.dao.querier.CriterionSet;
import gptma.bo.dao.querier.GenericQuerier;
import gptma.bo.dao.querier.JpaQuerier;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

import javax.inject.Inject;

import javax.persistence.EntityManager;

public class GenericDaoJpaImpl<T, PK extends Serializable> implements
		GenericDao<T, PK> {

	protected static final String HIBERNATE_QUERY_CACHE = "org.hibernate.cacheable";
	private Class<T> clazz;

	@Inject
	protected EntityManager em;

	public GenericDaoJpaImpl() {
		this.clazz = findDaoClass();
	}

	/**
	 * Some ClassCastExceptions with ParameterizedType instances with Weld
	 * which occurs on annotated DAOs (non-annotated ones are fine though)
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Class<T> findDaoClass() {
		Class<T> ret = null;
		
		Type type = getClass().getGenericSuperclass();
		if (!(type instanceof ParameterizedType)) {
			// annotated Weld bean
			type = getClass().getSuperclass().getGenericSuperclass();
		}
		
		if (type instanceof ParameterizedType) {
			ParameterizedType genericType = (ParameterizedType) type;
			ret = (Class<T>) genericType.getActualTypeArguments()[0];
		}
		
		return ret;
	}
	

	@Override
	public T save(T newInstance) {
		return em.merge(newInstance);
	}

	@Override
	public void save(Collection<T> instances) {
		if (instances != null) {
			for (T object : instances) {
				save(object);
			}
		}
	}

	@Override
	public void delete(T instance) {
		if (em.contains(instance)) {
			em.remove(instance);
		} 
		else {
			em.remove(em.merge(instance));
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		StringBuilder query = new StringBuilder("select o from ").append(
				clazz.getSimpleName()).append(" o");
		return em.createQuery(query.toString()).getResultList();
	}

	@Override
	public T findById(PK primaryKey) {
		return em.find(clazz, primaryKey);
	}

	protected String generateContainsCriteria(String value) {
		return new StringBuilder("%").append(value).append("%").toString();
	}

	@Override
	public List<T> findByCriteria(CriterionSet criterionSet) {
		GenericQuerier<T> querier = new JpaQuerier<T>(em, clazz);
		return querier.findByCriteria(criterionSet);
	}

}
